﻿/**
* @projectName   07-05-decode_audio
* @brief         解码音频，主要的测试格式aac和mp3
* @author        Liao Qingfu
* @date          2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavutil/frame.h>
#include <libavutil/mem.h>

#include <libavcodec/avcodec.h>
#include<libavformat/avformat.h>

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096

#define BUF_SIZE 20480






int read_packet(void *opaque,uint8_t *buf,int buf_size){
    FILE *in_file = (FILE*) opaque;
    int read_size = fread(in_file,1,buf_size,in_file);
    printf("read_packet read_size:%d,buf_size:%d\n",read_size,buf_size);

    if(read_size<0){
        return AVERROR_EOF;
    }
    return read_size;
}



static void decode(AVCodecContext *dec_ctx,AVPacket *packet,AVFrame * frame,FILE* outfile){
    int ret = 0;
    ret = avcodec_send_packet(dec_ctx,packet);
    if(ret == AVERROR(EAGAIN)){
        //错误处理

    }
    else if(ret<0)
    {
        //错误处理
        return ;
    }

    while(ret >= 0){
        ret = avcodec_receive_frame(dec_ctx,frame);
        if(ret < 0){
            exit(1);
        }

        if(!packet){
            printf("get flush frame\n");
        }

       //读出来以后
        int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);

        //并不是每一种音频解码器解出来，通用要调用重采样的函数去实现，这里只对ACC处理
        for(int i=0;i<frame->nb_samples;i++){
            for(int ch = 0;ch < dec_ctx->channels;ch++){
                fwrite(frame->data[ch]+data_size*i,1,data_size,outfile);
            }
        }



    }

}







// 播放范例：   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
int main(int argc, char **argv)
{
    if (argc != 3)
    {
        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
        exit(0);
    }

    const char *outfilename;
    const char *infilename;
    AVCodecParserContext *parser = NULL;
    int len = 0;
    int ret = 0;
    FILE *in_file = NULL;
    FILE *out_file = NULL;
    uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data = NULL;
    size_t   data_size = 0;
    AVPacket *pkt = NULL;
    AVFrame *decoded_frame = NULL;


    infilename    = argv[1];
    outfilename = argv[2];

    //1 打开参数
    in_file = fopen(infilename,"rb");
    if(!in_file){
        //进行报错处理这里直接省去
        return -1;
    }
    out_file= fopen(outfilename,"wb");
    if(!out_file){
        //进行报错处理这里直接省去
        return -1;
    }

    //2 自定义io
    //这里为avio_ct提前定义一些变量
    uint8_t *io_buffer = av_malloc(BUF_SIZE);//为io缓冲区分配内存
    AVIOContext *avio_ct = avio_alloc_context(io_buffer,
                                              BUF_SIZE,//缓存区大小
                                              0,//不支持写操作
                                              (void*)in_file,//传递输入文件给read_packet
                                              read_packet,//自定义读操作
                                              NULL,//不支持写操作
                                              NULL);//不支持读取字节位置操作
    AVFormatContext *format_ctx = avformat_alloc_context();
    format_ctx->pb = avio_ct;//通过设置 pb 字段，FFmpeg 将会使用之前定义的自定义读取操作（即通过 read_packet 函数读取数据）来从文件中读取数据，而不是使用默认的文件读取方式
    ret = avformat_open_input(&format_ctx,//指向 AVFormatContext 的指针。此函数将使用 format_ctx 来存储输入流的相关信息。
                              NULL,
                              NULL,
                              NULL);
    if(!ret){
        //进行错误处理这里省去
        return -1;
    }

    //编码器查找
    AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
    if(!codec){
        //进行错误处理这里省去
        return -1;
    }

    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    if(!codec_ctx){
        //进行错误处理这里省去
        return -1;
    }

    ret = avcodec_open2(codec_ctx,codec,NULL);
    if(!ret){
        //进行错误处理这里省去
        return -1;
    }
    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();


    while(1){
        ret = av_read_frame(format_ctx,packet);
        if(!ret){
            //进行错误处理这里省去
            //如果使用后网络需要判断可能是由于超时引起的
            break;
        }
         decode(codec_ctx,packet,frame,out_file);
    }

    //读取完成数据以后，冲刷编码器
    printf("read finsh");
    decode(codec_ctx,NULL,frame,out_file);

    fclose(in_file);
    fclose(out_file);

    av_free(io_buffer);
    av_frame_free(frame);
    av_packet_free(packet);

    avformat_close_input(&format_ctx);
    avcodec_free_context(&codec_ctx);









    printf("main finish, please enter Enter and exit\n");
    return 0;
}
